unit QBFloatPanel;

interface

{$I QBuilderVerCtrl.inc}

uses
  {$IFDEF VCL16}
    Winapi.Windows,
    Winapi.Messages,
    Vcl.StdCtrls,
    Vcl.Buttons,
    Vcl.Menus,
    Vcl.Controls,
    System.Classes,
    Vcl.Forms,
    System.SysUtils,
    Vcl.Graphics,
    Data.Db,
  {$ELSE}
    Windows,
    Messages,
    StdCtrls,
    Buttons,
    Menus,
    Controls,
    Classes,
    Forms,
    SysUtils,
    Graphics,
    Db,
  {$ENDIF}
  {$IFDEF USE_DEVEXPRESS}
    QBClasses_DEVEXP,
  {$ELSE}
    QBClasses,
  {$ENDIF}
  QBParser,
  QBStringTypes;

const
  CM_ACTIVECONTROL = CM_BASE + 101;
  CM_DEACTIVATECONTROL = CM_BASE + 102;
  CM_MOVECONTROL = CM_BASE + 103;
  CM_RESIZECONTROL = CM_BASE + 104;
  CM_CLOSETABLE = CM_BASE + 105;
  CM_UNLINKTABLE = CM_BASE + 106;
  CM_DELETELINK = CM_BASE + 107;

  iImageRes = 16;
  iVertSize = 5000;
  iHorzSize = 5000;
  iLinkWidth = 3;
  iArrowWidth = 4;

var
  fmLinkProp: TForm;
type
  TQBWorkArea = class;

  TQBAddEvent = procedure(Sender: TObject; Item: TObject) of object;
  TQBRemoveEvent = procedure(Sender: TObject; Item: TObject) of object;
  TQBChangeStringEvent = procedure(Item: TObject; const OldValue: aqbString) of object;

  TQBBorderKind = (qbkBump, qbkEtched, qbkRaised, qbkSunken);

  TTableHitTest = (htTOutSide, htTLeft, htTTop, htTRight, htTBottom, htTLeftTop,
    htTLeftBottom, htTRightTop, htTRightBottom, htTCaption, htTClient,
    htTEndLine, htTPoint, htTbutton, htTCheck);
  TSetOfTableHits = set of TTableHitTest;

  TQBSelectionLinkEvent = procedure(Sender: TObject; Table1, Table2: Pointer;
    Item1, Item2: Pointer; State: Bool) of object;
  TQBCheckItemEvent = procedure(Sender, Item: Pointer) of object;

  TQBEdit = class(TEdit)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

  TQBObjectArrangement = class(TCollectionItem)
  private
    FAlias: aqbString;
    FTop: integer;
    FLeft: integer;
    FWidth: integer;
    FHeight: integer;
    FExpanded: boolean;
  public
    property Alias: aqbString read FAlias write FAlias;
    property Top: integer read FTop write FTop;
    property Left: integer read FLeft write FLeft;
    property Width: integer read FWidth write FWidth;
    property Height: integer read FHeight write FHeight;
    property Expanded: boolean read FExpanded write FExpanded;
  end;

  TQBObjectArrangements = class(TCollection)
  private
    function GetItem(Index: Integer): TQBObjectArrangement;
    procedure SetItem(Index: Integer; const Value: TQBObjectArrangement);
  public
    function Add: TQBObjectArrangement;
    function IndexOf(const Alias: aqbString): integer;
    property Items[Index: Integer]: TQBObjectArrangement read GetItem
    write SetItem; default;
  end;

  TQBFloatObject = class(TObject) //Base class with abstract metods
  private
    FParent: TQBWorkArea;
    FSelection: Boolean;
    FOnMouseDown, FOnMouseUp: TMouseEvent;
    FOnMouseMove: TMouseMoveEvent;
    FOnKeyDown, FOnKeyUp: TKeyEvent;
  protected
    procedure SetSelect(Value: Boolean); virtual;
    function GetHint: aqbString; virtual; abstract;
  public
    constructor Create(AParent: TQBWorkArea);
    procedure Paint(DC: HDC); virtual; abstract;
    procedure MouseDown(var Msg: TWMMouse; Hit: TTableHitTest); virtual;
    procedure MouseMove(var Msg: TWMMouse; Hit: TTableHitTest); virtual;
    procedure MouseUp(var Msg: TWMMouse; Hit: TTableHitTest); virtual;
    procedure KeyDown(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyUp(var Key: Word; Shift: TShiftState); virtual;
    procedure LeaveHit(var Msg: TWMMouse; Hit: TTableHitTest); virtual;
    function HitTest(Pt: TPoint): TTableHitTest; virtual; abstract;
    procedure PopupMenu(var Menu: TPopupMenu; X, Y: Integer; Hit: TTableHitTest);
      virtual; abstract;
    property Parent: TQBWorkArea read FParent;
    property Hint: aqbString read GetHint;
    property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown;
    property OnMouseUp: TMouseEvent read FOnMouseUp write FOnMouseUp;
    property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove;
    property OnKeyDown: TKeyEvent read FOnkeyDown write FOnkeyDown;
    property OnKeyUp: TKeyEvent read FOnKeyUp write FOnKeyUp;
    property Selection: Boolean read FSelection write SetSelect;
  end;
  TQBFloatObjectClass = class of TQBFloatObject;

  TQBWorkArea = class(TScrollingWinControl)
  private
    FJoins: TList;
    FLinks: TList;
    FTables: TList;
    FHit: TTableHitTest;
    FProcessHit: TSetOfTableHits;
    FCurrentObj, FSelectionObj, FTargetDrag, FTargetObj, FHiliteItem: Pointer;
    FmovingX, FmovingY, FOldX, FOldY: Integer;
    FDragPoint: TPoint;
    FPopupMenu: TPopupMenu;
    FDefDrag, FDragCur, FExDragCur: HCursor;
    FCursorBMP: TBitmap;
    FFont: TFont;
    FHint: THintWindow;
    FOnAddLink: TQBAddEvent;
    FOnRemoveLink: TQBRemoveEvent;
    FOnSelectLink: TQBSelectionLinkEvent;
    FOnCheckingItem: TQBCheckItemEvent;
    FOnAddTable: TQBAddEvent;
    FOnRemoveTable: TQBRemoveEvent;
    FOnChangeAlias: TQBChangeStringEvent;
    FPen: TPen;
    FScrollBar: TScrollBar;
    FEditName: TEdit;
    FFindField: TEdit;
    FUpdating: boolean;
    FObjSavedArrangements: TQBObjectArrangements;
    FCheckLinkFieldsType: boolean; // ksv

    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    procedure WMErase(var Message: TMessage); message WM_ERASEBKGND;
    procedure WMMouseDown(var Message: TWMMouse); message wm_LButtonDown;
    procedure WMMouseMove(var Message: TWMMouse); message wm_MouseMove;
    procedure WMMouseUp(var Message: TWMMouse); message wm_LButtonUp;
    procedure WMMousePopup(var Message: TWMMouse); message wm_RButtonUp;
    procedure WMDblClick(var Message: TWMMouse); message wm_LButtonDblClk;
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
    procedure WMTimer(var Message: TMessage); message WM_TIMER;
    procedure CMCloseTable(var Message: TMessage); message CM_CLOSETABLE;
    procedure CMUnlinkTable(var Message: TMessage); message CM_UNLINKTABLE;
    procedure CMDeleteLink(var Message: TMessage); message CM_DELETELINK;
    procedure CMMoveControl(var Message: TMessage); message CM_MOVECONTROL;
    procedure CMResizeControl(var Message: TMessage); message CM_RESIZECONTROL;

    procedure SetupCursor(AHit: TTableHitTest);
    procedure SetFont(const Value: TFont);
    function GetParser: TQBSelectParser;
    function GenerateUniqueAlias(const Alias, Prefix: aqbString): aqbString;
  protected
    function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    procedure DoStartDrag(var DragObject: TDragObject); override;
    procedure DoEndDrag(Target: TObject; X, Y: Integer); override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure HandleEditNameKillFocus(Sender: TObject);
    procedure HandleEditNameKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure HandleFindFieldKillFocus(Sender: TObject);
    procedure HandleFindFieldChange(Sender: TObject);
    procedure RemoveLink(Item: TObject; Index: Integer; Refresh: Boolean);
    procedure HandleScrollTableContents(Sender: TObject);
    procedure HandleKillFocus(Sender: TObject);
    procedure HandleDragOver(Sender, Source: TObject; X, Y: Integer; State:
      TDragState; var Accept: Boolean);
    procedure HandleDragEnd(Sender, Target: TObject; X, Y: Integer);
    procedure HandleDragDrop(Sender, Source: TObject; X, Y: Integer);
    function GetDragFieldName: Boolean;
    function GetFont: TFont;
    procedure HandleSetFont(Value: TFont);
    function SameIdentifier(const Ident1, Ident2: aqbString): boolean;
    function AddPanelInternal(const AName, AAlias: aqbString; ADataSet: TDataSet;
      UseDefaultPos: boolean; ATopLeft: TPoint): Pointer;
    function AddProcInternal(const AName, AAlias: aqbString; ADataSet: TDataSet;
      AParams: TParams; UseDefaultPos: boolean; ATopLeft: TPoint): Pointer;
  public
    TblButtons: array[0..2] of TBitmap;
    CheckBox: array[0..1] of TBitmap;
    InputIcon: array[0..1] of TBitmap;
    SelectBox: array[0..2] of TBitmap;

    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DoEnterParameter(SProcedure, Item: TObject);
    procedure HandleCheckItem(Sender: TObject; Item: Pointer);
    procedure SelectLink(Item1, Item2: Pointer; State: Bool);
    procedure BeginUpdate;
    procedure EndUpdate;
    function AddLink(Table1, Table2: Pointer; Item1, Item2: Pointer): Pointer;
    function AddPanel(const AName, AAlias: aqbString; ADataSet: TDataSet): Pointer;
    function AddPanelAt(const AName, AAlias: aqbString; ADataSet: TDataSet; ATopLeft: TPoint): Pointer;
    function AddProc(const AName, AAlias: aqbString; ADataSet: TDataSet;
      AParams: TParams): Pointer;
    //igorp
    procedure CheckSubQueryTable(AName: aqbString; ADataSet: TDataSet;
      IsAdd: Boolean);
    //\igorp
    function AddProcAt(const AName, AAlias: aqbString; ADataSet: TDataSet;
      AParams: TParams; ATopLeft: TPoint): Pointer;
    procedure ShowPanel(APanel: TObject);
    function FindJoin(Table1, Table2: Pointer; out Reverse: Boolean): TObject;
    function FindLink(Table1, Table2: Pointer; Item1, Item2: Pointer): Pointer;
    function FindLinkByID(const ID: aqbString): integer;
    procedure TableToFront(Table: TObject);
    function GetTable(const TableName: aqbString): Pointer;
    procedure ClearContents;
    procedure ClearSelection;
    procedure CloseNameEditor;
    property SelectionObject: Pointer read FSelectionObj write FSelectionObj;
    property OnAddLink: TQBAddEvent read FOnAddLink write FOnAddLink;
    property OnRemoveLink: TQBRemoveEvent read FOnRemoveLink write FOnRemoveLink;
    property OnSelectLink: TQBSelectionLinkEvent read FOnSelectLink write
      FOnSelectLink;
    property OnCheckingItem: TQBCheckItemEvent read FOnCheckingItem write
      FOnCheckingItem;
    property OnAddTable: TQBAddEvent read FOnAddTable write FOnAddTable;
    property OnRemoveTable: TQBRemoveEvent read FOnRemoveTable write
      FOnRemoveTable;
    property OnChangeAlias: TQBChangeStringEvent read FOnChangeAlias write
      FOnChangeAlias;
    property DragFieldName: Boolean read GetDragFieldName;
    property Tables: TList read FTables;
    property Links: TList read FLinks;
    property Font: TFont read GetFont write SetFont;
    property Pen: TPen read FPen;
    property ScrollBar: TScrollBar read FScrollBar;
    property EditName: TEdit read FEditName;
    property FindField: TEdit read FFindField;
    property IsUpdating: boolean read FUpdating;
    property Parser: TQBSelectParser read GetParser;
    // ksv
    property CheckLinkFieldsType: boolean read FCheckLinkFieldsType
      write FCheckLinkFieldsType default true;
    // \ksv
  end;

implementation

uses
  {$IFDEF VCL16}
    System.Types,
  {$ELSE}
    {$IFDEF VCL9}
      Types,
    {$ENDIF}
  {$ENDIF}

  {$IFNDEF USE_DEVEXPRESS}
    QBLinkProp,
  {$ELSE}
    QBLinkProp_DEVEXP,
  {$ENDIF}
  {$IFDEF EC_UNICODE}
    ecUnicode,
  {$ENDIF}
  QBLinks,
  QBFloatTable,
  QBWindow,
  QBProc,
  QBResource;

{$R *.RES}

{ TQBEdit }

procedure TQBEdit.CreateParams(var Params: TCreateParams);
begin
  inherited;
  with Params do
  begin
    if (Style and WS_BORDER) = 0 then
      Style := Style or WS_BORDER;
  end;
end;

{ TQBWorkArea }

destructor TQBWorkArea.Destroy;
var
  I: Integer;
  Link: TQBLink;
begin
  for I := 0 to 2 do
    TblButtons[I].Free;
  for I := 0 to 1 do
    CheckBox[I].Free;
  for I := 0 to 1 do
    InputIcon[I].Free;
  for I := 0 to 2 do
    SelectBox[I].Free;

  FSelectionObj := nil;
  for I := 0 to FLinks.Count - 1 do
  begin
    Link := FLinks.Items[0];
    RemoveLink(Link, 0, false);
  end;
  FCursorBMP.Free;
  for I := 0 to FTables.Count - 1 do
    TQBTAble(FTAbles[i]).Free;
  FTables.Free;
  FJoins.Free;
  FLinks.Free;
  FFont.Free;
  FPen.Free;
  FObjSavedArrangements.Free;
  inherited;
end;

constructor TQBWorkArea.Create(AOwner: TComponent);
var
  I: Integer;
begin
  if not (AOwner is TCustomQueryBuilder) then
    raise Exception.Create(rsFloatPanelIsNotValidWorkArea); //??
  inherited;
  Parent := AOwner as TWinControl;
  FUpdating := False;

  {  TCustomQueryBuilder(AOwner).WorkAreaOptions.OnSetFont := _SetFont;}//!!
  ControlStyle := ControlStyle + [csDisplayDragImage];
  ShowHint := false;
  ParentShowHint := false;
  DragKind := dkDrag;
  DragMode := dmManual;
  OnDragOver := HandleDragOver;
  OnEndDrag := HandleDragEnd;
  OnDragDrop := HandleDragDrop;
  FProcessHit := [htTCaption, htTLeft, htTRight, htTTop, htTBottom, htTLeftTop,
    htTLeftBottom,
    htTRightTop, htTRightBottom];
  FCurrentObj := nil;
  FSelectionObj := nil;
  FTargetObj := nil;
  FDefDrag := Screen.Cursors[-12];
  FDragCur := LoadCursor(HInstance, 'DragCursor');

  DisableAutoRange;
  VertScrollBar.Range := iVertSize;
  HorzScrollBar.Range := iHorzSize;

  FPen := TPen.Create;
  FPen.Color := clBlack;
  FFont := TFont.Create;
  //  FFont.Assign(TCustomQueryBuilder(Parent).WorkAreaOptions.Font); //!!
  FHint := THintWindow.Create(Self);
  //  FHint.Parent := Self;
  FHint.ParentWindow := Application.Handle;
  FHint.Color := clInfoBk;

  FCursorBMP := TBitMap.Create;
  FCursorBMP.PixelFormat := pf1bit;
  FCursorBMP.Canvas.Brush.Style := bsSolid;
  //  FCursorBMP.Canvas.CopyMode:=cmSrcInvert	;
  FCursorBMP.Canvas.Font := Font;
  FCursorBMP.Canvas.Pen.Color := clWhite;
  FCursorBMP.Canvas.Brush.Color := clBlack;
  FCursorBMP.Canvas.Font.Style := [fsBold];
  FCursorBMP.Canvas.Font.Color := clWhite;

  FLinks := TList.Create();
  FJoins := TList.Create();
  FTables := TList.Create();
  FPopupMenu := TPopupMenu.Create(Self);

  for I := 0 to 2 do
  begin
    TblButtons[I] := TBitMap.Create;
    TblButtons[I].LoadFromResourceName(HInstance, 'BTN' + IntToStr(I + 1));
  end;
  CheckBox[0] := TBitMap.Create;
  CheckBox[0].LoadFromResourceName(HInstance, 'UNCHECKBMP');
  CheckBox[1] := TBitMap.Create;
  CheckBox[1].LoadFromResourceName(HInstance, 'CHECKBMP');
  InputIcon[0] := TBitMap.Create;
  InputIcon[0].LoadFromResourceName(HInstance, 'INPUTNULL');
  InputIcon[1] := TBitMap.Create;
  InputIcon[1].LoadFromResourceName(HInstance, 'INPUTPARAM');
  for I := 0 to 2 do
  begin
    SelectBox[I] := TBitMap.Create;
    SelectBox[I].LoadFromResourceName(HInstance, 'BOX' + IntToStr(I + 1));
  end;

  FEditName := TEdit.Create(Self);
  FEditName.Parent := Self;
  FEditName.Visible := false;
  FEditName.Color := TCustomQueryBuilder(Parent).Palette.ActiveCaption;
  FEditName.OnExit := HandleEditNameKillFocus;
  FEditName.OnKeyUp := HandleEditNameKeyUp;
  FEditName.BorderStyle := bsNone;

  FFindField := TQBEdit.Create(Self);
  FFindField.Parent := Self;
  FFindField.Visible := false;
  FFindField.AutoSelect := false;
  FFindField.Height := 15;
  FFindField.Color := Brush.Color;
  FFindField.OnExit := HandleFindFieldKillFocus;
  FFindField.OnChange := HandleFindFieldChange;
  FFindField.BorderStyle := bsNone;

  FScrollBar := TScrollBar.Create(Self);
  FScrollBar.Parent := Self;
  FScrollBar.Visible := false;
  FScrollBar.Kind := sbVertical;
  FScrollBar.OnEnter := HandleKillFocus;
  FScrollBar.OnChange := HandleScrollTableContents;

  {
  fmLinkProp := TfmLinkProp.Create(Self);
  if TCustomQueryBuilder(AOwner).Parser <> nil then
  begin
    TfmLinkProp(fmLinkProp).FullOuterJoinSupported :=
      TCustomQueryBuilder(AOwner).Parser.IsFullOuterJoinSupported;
    if TCustomQueryBuilder(AOwner).Parser.Scanner <> nil then
      TfmLinkProp(fmLinkProp).CompareOperatorList :=
        TCustomQueryBuilder(AOwner).Parser.Scanner.CommaJoinOperators;
  end;
  }
  FObjSavedArrangements := TQBObjectArrangements.Create(TQBObjectArrangement);
  FCheckLinkFieldsType := true;
end;

procedure TQBWorkArea.CMMoveControl(var Message: TMessage);
var
  I, Mode: Integer;
  Link: TQBLink;
  Rect: TRect;
  Table: TQBTable;
  Rgn, LinkRgn: HRGN;
  DC: HDC;
begin
  Table := Pointer(Message.wParam);

  for I := 0 to FLinks.Count - 1 do
  begin
    Mode := -1;
    Link := FLinks.Items[I];
    if Link.Join.Table[0] = Table then
      Mode := 1;
    if Link.Join.Table[1] = Table then
      Mode := 2;
    if (Mode <> -1) and (Link.CountPoints = 4) then
      Mode := 0;
    if Mode <> -1 then
    begin
      Rgn := Link.ClipRgn[Mode];
      InvalidateRgn(Handle, Rgn, False);
      DeleteObject(Rgn);
      Link.SetBoundaryRect();
    end;
  end;
  Rect := Table.BoundsRect;
  OffsetRect(Rect, -HorzScrollBar.Position, -VertScrollBar.Position);
  ValidateRect(Handle, @Rect);
  UpdateWindow(Handle);
  DC := GetWindowDC(Handle);
  Rgn := CreateRectRgn(0, 0, ClientWidth, ClientHeight);
  LinkRgn := CreateRectRgn(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
  CombineRgn(Rgn, Rgn, LinkRgn, RGN_DIFF);
  DeleteObject(LinkRgn);
  SelectObject(DC, Rgn);
  DeleteObject(Rgn);
  try
    SelectObject(DC, GetStockObject(BLACK_BRUSH));
    for I := 0 to FLinks.Count - 1 do
    begin
      Link := FLinks.Items[I];
      if (Link.Join.Table[0] = Table) or (Link.Join.Table[1] = Table) then
        Link.Paint(DC);
    end;
  finally
    ReleaseDC(Handle, DC);
  end; (**)
end;

procedure TQBWorkArea.CMResizeControl(var Message: TMessage);
var
  I, xPos, yPos: Integer;
  Link: TQBLink;
  Table: TQBTable;
  Rect: TRect;
  Rgn: HRGN;
begin
  xPos := HorzScrollBar.Position;
  yPos := VertScrollBar.Position;
  Table := Pointer(Message.wParam);
  for I := 0 to FLinks.Count - 1 do
  begin
    Link := FLinks.Items[I];
    if (Link.Join.Table[0] = Table) or (Link.Join.Table[1] = Table) then
    begin
      Rgn := Link.ClipRgn[0];
      InvalidateRgn(Handle, Rgn, False);
      DeleteObject(Rgn);
      Link.SetBoundaryRect;
      Rgn := Link.ClipRgn[0];
      InvalidateRgn(Handle, Rgn, False);
      DeleteObject(Rgn);
    end;
  end;
  for I := 0 to FTables.Count - 1 do
  begin
    Table := FTables.Items[I];
    Rect := Table.BoundsRect;
    OffsetRect(Rect, -xPos, -yPos);
    ValidateRect(Handle, @Rect);
  end;
  UpdateWindow(Handle);
end;

function TQBWorkArea.GenerateUniqueAlias(const Alias, Prefix: aqbString): aqbString;
var
  Tbl: TQBTable;
  I, Cnt: Integer;
  BaseAlias: aqbString;
begin
  BaseAlias := Alias;
  Result := Alias;
  I := Pos('.', BaseAlias);
  while I <> 0 do
  begin
    if I = Length(BaseAlias) then
      Delete(BaseAlias, I, 1)
    else
      Delete(BaseAlias, 1, I);
    I := Pos('.', BaseAlias);
  end;
  if (TCustomQueryBuilder(Parent).Parser <> nil) and
    (TCustomQueryBuilder(Parent).Parser.Scanner <> nil) then
    for I := Length(BaseAlias) downto 1 do
      if TCustomQueryBuilder(Parent).Parser.Scanner.IsIdentQuote(BaseAlias[I]) then
        Delete(BaseAlias, I, 1);
  if Trim(BaseAlias) = '' then
  begin
    if (TCustomQueryBuilder(Parent).Parser <> nil) and
      TCustomQueryBuilder(Parent).Parser.IsLowerCaseAliases then
      BaseAlias := QBLowerCase(Prefix)
    else
      BaseAlias := Prefix;
    BaseAlias := BaseAlias + '1';
  end;

  Cnt := 0;
  repeat
    for I := 0 to FTables.Count - 1 do
    begin
      Tbl := FTables.Items[I];
      if Tbl.Alias = Result then
      begin
        Inc(Cnt);
        Result := BaseAlias + IntToStr(Cnt);
        Break;
      end;
    end;
  until I >= FTables.Count;
end;

function TQBWorkArea.AddPanel(const AName, AAlias: aqbString; ADataSet: TDataSet): Pointer;
begin
  Result := AddPanelInternal(AName, AAlias, ADataset, true, Point(0, 0));
end;

function TQBWorkArea.AddProc(const AName, AAlias: aqbString; ADataSet: TDataSet; AParams:
  TParams): Pointer;
begin
  Result := AddProcInternal(AName, AAlias, ADataSet, AParams, true, Point(0, 0));
end;

procedure TQBWorkArea.ShowPanel(APanel: TObject);
var
  Rect: TRect;
begin
  if not (APanel is TQBTable) then
    Exit;
  if (APanel as TQBTable).Parent <> Self then
    Exit;

  Rect := (APanel as TQBTable).BoundsRect;
  Dec(Rect.Left, HorzScrollBar.Margin);
  Inc(Rect.Right, HorzScrollBar.Margin);
  Dec(Rect.Top, VertScrollBar.Margin);
  Inc(Rect.Bottom, VertScrollBar.Margin);
  if Rect.Left < 0 then
    with HorzScrollBar do
      Position := Position + Rect.Left
  else if Rect.Right > ClientWidth then
  begin
    if Rect.Right - Rect.Left > ClientWidth then
      Rect.Right := Rect.Left + ClientWidth;
    with HorzScrollBar do
      Position := Position + Rect.Right - ClientWidth;
  end;
  if Rect.Top < 0 then
    with VertScrollBar do
      Position := Position + Rect.Top
  else if Rect.Bottom > ClientHeight then
  begin
    if Rect.Bottom - Rect.Top > ClientHeight then
      Rect.Bottom := Rect.Top + ClientHeight;
    with VertScrollBar do
      Position := Position + Rect.Bottom - ClientHeight;
  end;
end;

function TQBWorkArea.FindJoin(Table1, Table2: Pointer; out Reverse: Boolean):
  TObject;
var
  I: Integer;
  Join: TQBJoin;
  IsInJoins1, IsInJoins2: Boolean; // ayz
begin
  Reverse := false;
  // ayz
  IsInJoins1 := False;
  IsInJoins2 := False;
  // \ayz
  for I := 0 to FJoins.Count - 1 do
  begin
    Join := FJoins.Items[I];
    if (Join.Table[0] = Table1) and (Join.Table[1] = Table2) then
    begin
      Result := Join;
      Exit;
    end
    else if (Join.Table[0] = Table2) and (Join.Table[1] = Table1) then
    begin
      Result := Join;
      Reverse := true;
      Exit;
    end;
    // ayz
    IsInJoins1 := IsInJoins1 or (Join.Table[0] = Table1) or (Join.Table[1] = Table1);
    IsInJoins2 := IsInJoins2 or (Join.Table[0] = Table2) or (Join.Table[1] = Table2);
    // \ayz
  end;
  Reverse := not IsInJoins1 and IsInJoins2; // ayz
  Result := nil;
end;

function TQBWorkArea.FindLink(Table1, Table2: Pointer; Item1, Item2:
  Pointer): Pointer;
var
  I: Integer;
  Link: TQBLink;
  Join: TQBJoin;
  Rev: Boolean;
begin
  Result := nil;
  for I := 0 to FLinks.Count - 1 do
  begin
    Link := FLinks.Items[I];
    Join := TQBJoin(FindJoin(Table1, Table2, Rev));
    if (Link.Join = Join) and
      ((not Rev and (Link.Field1 = Item1) and (Link.Field2 = Item2)) or
      (Rev and (Link.Field1 = Item2) and (Link.Field2 = Item1))) then
    begin
      Result := Link;
      Break;
    end;
  end;
end;

procedure TQBWorkArea.TableToFront(Table: TObject);
var
  I, Ind: Integer;
  P: TQBTable;
begin
  Ind := FTables.IndexOf(Table);
  if (Ind >= FTables.Count) or (Ind < 1) then
    Exit;
  for I := Ind downto 1 do
  begin
    P := FTables.Items[I - 1];
    FTables.Items[I - 1] := FTables.Items[I];
    FTables.Items[I] := P;
  end;
end;

function TQBWorkArea.GetTable(const TableName: aqbString): Pointer;
var
  I: Integer;
  Tbl: TQBTable;
begin
  Result := nil;
  if Trim(TableName) = '' then
  begin
    if FTables.Count = 1 then
      Result := FTables.Items[0];
    Exit;
  end;
  for I := 0 to FTables.Count - 1 do
  begin
    Tbl := FTables.Items[I];
    if Tbl.Alias = TableName then
    begin
      Result := Tbl;
      Exit;
    end;
  end;
end;

procedure TQBWorkArea.CheckSubQueryTable(AName: aqbString;
  ADataSet: TDataSet; IsAdd: Boolean);
var
  FullQB: TFullQueryBuilder;
begin
  FullQB := TCustomQueryBuilder(Parser.Parent).GetOwnFullQueryBuilder;
  if AName = '' then
    FullQB.CheckSubQueryTable(TCustomQueryBuilder(Parser.Parent).Alias,
      ADataSet, True)
  else
    FullQB.CheckSubQueryTable(AName, ADataSet, False);
end;

procedure TQBWorkArea.ClearContents;
var
  Obj: TQBFloatObject;
begin
  FCurrentObj := nil;
  FSelectionObj := nil;
  if FScrollBar.Visible then
    FScrollBar.Visible := False;
  if FEditName.Visible then
    FEditName.Visible := False;
  while FLinks.Count > 0 do
  begin
    Obj := FLinks.Items[0];
    FLinks.Delete(0);
    Obj.Free;
  end;
  while FJoins.Count > 0 do
  begin
    TObject(FJoins.Items[0]).Free;
    FJoins.Delete(0);
  end;
  FObjSavedArrangements.Clear;
  while FTables.Count > 0 do
  begin
    Obj := FTables.Items[0];
    with FObjSavedArrangements.Add do
    begin
      Left := TQBTable(Obj).Left;
      Top := TQBTable(Obj).Top;
      Width := TQBTable(Obj).Width;
      Height := TQBTable(Obj).Height;
      Alias := TQBTable(Obj).Alias;
      Expanded := TQBTable(Obj).Expand;
    end;
    FTables.Delete(0);
    Obj.Free;
  end;
end;

type
  TQBSelectParserAccess = class(TQBSelectParser);

function TQBWorkArea.AddLink(Table1, Table2: Pointer; Item1, Item2: Pointer):
  Pointer;
var
  Link: TQBLink;
  Join: TQBJoin;
  Rev, NewJoin: Boolean;
  Tbl1, Tbl2: TQBFloatObject;
  JoinDef: TQBJoinDef;
begin
  Result := nil;
  Tbl1 := Table1;
  Tbl2 := Table2;
  if not ((Tbl1 is TQBTable) and (Tbl2 is TQBTable)) then
    Exit;
  NewJoin := false;
  Join := TQBJoin(FindJoin(Table1, Table2, Rev));
  if not Assigned(Join) then
  begin
    if Rev then
      Join := TQBJoin.CreateJoin(Self, Table2, Table1)
    else
      Join := TQBJoin.CreateJoin(Self, Table1, Table2);
    FJoins.Add(Join);
    NewJoin := True;
  end;
  if Rev then
    Link := TQBLink.CreateLink(Self, Join, Item2, Item1) // iplus
  else
    Link := TQBLink.CreateLink(Self, Join, Item1, Item2);

  if not TCustomQueryBuilder(Parent).DoAddRemoveLink(Link, qbaAdd) then
  begin
    Link.Free;
    if NewJoin then
    begin
      FJoins.Delete(FJoins.Count - 1);
      Join.Free;
    end;
    Exit;
  end;
  FLinks.Add(Link);
  if not Assigned(Parser.JoinDefs.Find(TQBTable(Tbl1).Alias,
    TQBListItem(Item1).Caption, TQBTable(Tbl2).Alias,
    TQBListItem(Item2).Caption)) then
  begin
    JoinDef := Parser.JoinDefs.Add;

    if not Rev then
    begin // iplus
      JoinDef.TableLeft := TQBTable(Tbl1).Alias;
      JoinDef.FieldLeftName := TQBListItem(Item1).Caption;
      JoinDef.TableRight := TQBTable(Tbl2).Alias;
      JoinDef.FieldRightName := TQBListItem(Item2).Caption;
    end
    else
    begin
      JoinDef.TableLeft := TQBTable(Tbl2).Alias;
      JoinDef.FieldLeftName := TQBListItem(Item2).Caption;
      JoinDef.TableRight := TQBTable(Tbl1).Alias;
      JoinDef.FieldRightName := TQBListItem(Item1).Caption;
    end;
    { was
    JoinDef.TableLeft := TQBTable(Tbl1).Alias;
    JoinDef.FieldLeftName := TQBListItem(Item1).Caption;
    JoinDef.TableRight := TQBTable(Tbl2).Alias;
    JoinDef.FieldRightName := TQBListItem(Item2).Caption;
    }
    JoinDef.Kind := Join.Kind;
    JoinDef.BindJoin;
  end;
  if Assigned(FOnAddLink) then
    FOnAddLink(Self, Link);
  Result := Link;
end;

procedure TQBWorkArea.HandleEditNameKillFocus(Sender: TObject);
var
  OldAlias, NewAlias: aqbString;
  PosOfP, I: Integer;
  Tbl, Obj: TQBFloatObject;
begin
  FEditName.Visible := False;
  Tbl := FSelectionObj;
  if (Tbl = nil) or not (Tbl is TQBTable) then
    Exit;
  with FEditName do
  begin
    OldAlias := (Tbl as TQBTable).Alias;
    NewAlias := Text;
    //Lokking for same alias
    for I := 0 to FTables.Count - 1 do
    begin
      Obj := FTables.Items[I];
      if (Obj <> Tbl) and ((Obj as TQBTable).Alias = NewAlias) then
        raise Exception.Create(rsFloatPanelTableWithAlias + ' ' + NewAlias +
          ' ' + rsFloatPanelAlreadyExists);
    end;
    if NewAlias = (Tbl as TQBTable).TableName then
      NewAlias := '';
    PosOfP := Pos('.', NewAlias);
    while PosOfP > 0 do
    begin
      Delete(NewAlias, PosOfP, 1);
      PosOfP := Pos('.', NewAlias);
    end;
    (Tbl as TQBTable).Alias := NewAlias;
    if Assigned(FOnChangeAlias) then
      FOnChangeAlias(Tbl, OldAlias);
  end;
end;

procedure TQBWorkArea.HandleEditNameKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = VK_ESCAPE then
  begin
    FEditName.OnExit := nil;
    FEditName.Visible := false;
    FEditName.OnExit := HandleEditNameKillFocus;
  end;
  if Key = VK_RETURN then
    FEditName.Visible := False;
end;

procedure TQBWorkArea.HandleFindFieldKillFocus(Sender: TObject);
begin
  FFindField.Visible := False;
  SetFocus();
end;

procedure TQBWorkArea.HandleFindFieldChange(Sender: TObject);
var
  TextOfLine: aqbString;
  DC: HDC;
  sz: TSize;
begin
  TextOfLine := FFindField.Text;
  if TextOfLine = '' then
    FFindField.Visible := false;
  DC := GetDC(FindField.Handle);
  try
    SelectObject(DC, FFindField.Font.Handle);
    GetTextExtentPoint32(DC, @TextOfLine[1], Length(TextOfLine), sz);
  finally
    ReleaseDC(FindField.Handle, DC);
  end;
  FFindField.Width := sz.cx + 20;
end;

procedure TQBWorkArea.RemoveLink(Item: TObject; Index: Integer; Refresh:
  Boolean);
var
  Link: TQBLink;
  Join: TQBJoin;
  I: Integer;
  Rgn: HRGN;
  Rect: TRect;
  JoinDef: TQBJoinDef;
  Field1, Field2: TQBListItem;
begin
  Link := TQBLink(Item);
  if FCurrentObj = Link then
    FCurrentObj := nil;
  if (Link = FSelectionObj) then
  begin
    Link.Selection := False;
    FSelectionObj := nil;
  end;
  if Refresh then
  begin
    Rgn := Link.ClipRgn[0];
    InvalidateRgn(Handle, Rgn, false);
  end;
  Field1 := Link.Field1;
  Field2 := Link.Field2;

  Join := Link.Join;

  if Assigned(TCustomQueryBuilder(Owner).Parser.TableDefs) then // ayz
  begin
    JoinDef := Parser.JoinDefs.Find(Join.Table[0].TableName,
      Link.Field1.Caption, Join.Table[1].Alias, Link.Field2.Caption);
    if Assigned(JoinDef) then
    begin
      Parser.JoinDefs.DeleteCorrespondedJoinsWithConst(JoinDef); // ayz
      Parser.JoinDefs.Delete(JoinDef.Index);
    end;
  end;

  FLinks.Delete(Index);
  if Assigned(FOnRemoveLink) then
    FOnRemoveLink(Self, Item);
  Item.Free();
  for I := 0 to FLinks.Count - 1 do
  begin
    Link := FLinks.Items[I];
    if Link.Join = Join then
      Exit; //There is one Link for Join in List
  end;
  I := FJoins.IndexOf(Join);
  if I <> -1 then
  begin
    FJoins.Delete(I);
    Join.Free;
  end;
  if Refresh and Assigned(Field1) and Assigned(Field2) then
  begin
    Field1.Selected := False;
    Rect := Field1.BoundsRect;
    InvalidateRect(Handle, @Rect, False);
    Field2.Selected := False;
    Rect := Field2.BoundsRect;
    InvalidateRect(Handle, @Rect, False);
  end;
end;

procedure TQBWorkArea.DoEnterParameter(SProcedure, Item: TObject);
var
  TableDef: TQBTableDef;
  ParamDef: TQBFieldDef;
begin
  if Assigned(TCustomQueryBuilder(Parent).OnEnterProcParameter) then
    TCustomQueryBuilder(Parent).OnEnterProcParameter(Parent, TQBTable(SProcedure),
      TQBListItem(Item));
  TableDef := TCustomQueryBuilder(Owner).Parser.TableDefs.FindByName(
    TQBTable(SProcedure).TableName);
  if Assigned(TableDef) then
  begin
    ParamDef := TableDef.FieldDefs.FindByName(TQBListItem(Item).Caption);
    if Assigned(ParamDef) then
      ParamDef.Value := TQBListItem(Item).VarValue
  end;
end;

procedure TQBWorkArea.HandleCheckItem(Sender: TObject; Item: Pointer);
begin
  if not (Sender is TQBTable) then
    Exit;
  if Assigned(FOnCheckingItem) then
    FOnCheckingItem(Sender, Item);
end;

procedure TQBWorkArea.SelectLink(Item1, Item2: Pointer; State: Bool);
var
  Fld1, Fld2: TQBListItem;
begin
  Fld1 := Item1;
  Fld2 := Item2;
  if Assigned(FOnSelectLink) then
    FOnSelectLink(Self, Fld1.Table, Fld2.Table, Fld1, Fld2, State);
end;

procedure TQBWorkArea.HandleScrollTableContents(Sender: TObject);
var
  Obj: TQBFloatObject;
begin
  Obj := FSelectionObj;
  if Obj is TQBTable then
  begin
    (Obj as TQBTable).FirstItem := TScrollBar(Sender).Position;  //FScrollBar.Position; igorp
    UpdateWindow(Handle);
    Perform(CM_RESIZECONTROL, Integer(Obj), 0);
  end;
end;

procedure TQBWorkArea.HandleKillFocus(Sender: TObject);
begin
  SetFocus;
end;

procedure TQBWorkArea.HandleDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
var
  SelectionItem, TargetItem: TQBListItem;
  Rect: TRect;
  I: Integer;
  Point: TPoint;
  Obj: TQBFloatObject;
  DC: HDC;
label
  Final;
begin
  Accept := false;
  TargetItem := nil;
  if Source <> Self then
  begin
    if Assigned(TCustomQueryBuilder(Parent).OnDragOver) then
    begin
      Point.x := X;
      Point.y := Y;
      MapWindowPoints(Handle, Parent.Handle, Point, 1);
      TCustomQueryBuilder(Parent).OnDragOver(Sender, Source, Point.x, Point.y, State,
        Accept);
    end;
    goto Final;
  end;
  Obj := FSelectionObj;
  if (Obj = nil) or not (Obj is TQBTable) then
    goto Final;
  SelectionItem := (Obj as TQBTable).CurrentItem;
  if SelectionItem = nil then
    goto Final;
  Point.x := X;
  Point.y := Y;
  for I := 0 to FTables.Count - 1 do
  begin
    Obj := FTables.Items[I];
    if Obj.HitTest(Point) = htTClient then
    begin
      if Obj = FSelectionObj then
        Break;
      FTargetObj := Obj;
      X := X + HorzScrollBar.Position;
      Y := Y + VertScrollBar.Position;
      Rect := (Obj as TQBTable).BoundsRect;
      if (Y < Rect.Top + 25) or (Y > Rect.Bottom - 5) then
      begin
        //Auto FScrollBar
        if State = dsDragMove then
        begin
          FDragPoint.x := X;
          FDragPoint.y := Y;
          if Y < Rect.Top + 25 then
            SetTimer(Handle, 1, 200, nil) //Call WMTIMER
          else
            SetTimer(Handle, 2, 200, nil)
        end
        else
        begin //Start/End FScrollBaring
          FDragPoint.x := 0;
          FDragPoint.y := 0;
        end;
      end;
      TargetItem := (Obj as TQBTable).GetItemAt(Point.x, Point.y);
      if (TargetItem = nil) or (TargetItem.ScopeType <> ptOutput) then
        Break;
      if CheckLinkFieldsType then
      begin
//  sFloat := [ftFloat, ftCurrency];
//  sNumeric := [ftSmallint, ftInteger, ftWord, ftAutoInc, ftLargeInt];
//  sString := [ftString, ftMemo, ftFmtMemo, ftFixedChar, ftWideString];
//  sDateTime := [ftDate, ftTime, ftDateTime];
        if SelectionItem.DataType in [ftSmallint, ftInteger, ftWord, ftAutoInc, ftLargeInt] then
        begin
          if not (TargetItem.DataType in [ftSmallint, ftInteger, ftWord, ftAutoInc, ftLargeInt]) then
            Break
        end
        else if SelectionItem.DataType in [ftFloat, ftCurrency] then
        begin
          if not (TargetItem.DataType in [ftFloat, ftCurrency]) then
            Break
        end
        else if SelectionItem.DataType in [ftString, ftMemo, ftFmtMemo, ftFixedChar, ftWideString{$IFDEF VCL10}, ftWideMemo{$ENDIF}] then
        begin
          if not (TargetItem.DataType in [ftString, ftMemo, ftFmtMemo, ftFixedChar, ftWideString{$IFDEF VCL10}, ftWideMemo{$ENDIF}]) then
            Break
        end
        else if SelectionItem.DataType <> TargetItem.DataType then
          Break;
      end;
      //Matching for same link
      if FindLink(FSelectionObj, Obj, SelectionItem, TargetItem) <> nil then
        Break;
      FTargetDrag := TargetItem;
      Accept := true;
      Break;
    end;
  end;
  Final:
  DC := GetDC(Handle);
  try
    SetBkMode(DC, TRANSPARENT);
    if (FHiliteItem <> nil) and (FHiliteItem <> TargetItem) then
    begin //Hide highlighting
      SelectionItem := FHiliteItem;
      Rect := SelectionItem.Table.ListItemRect[SelectionItem];
      SelectionItem.DrawItem(DC, Rect, [], true);
      FHiliteItem := nil;
    end;
    if TargetItem <> nil then
    begin
      Rect := TargetItem.Table.ListItemRect[TargetItem];
      TargetItem.DrawItem(DC, Rect, [isDroped]);
      FHiliteItem := TargetItem;
    end;
  finally
    ReleaseDC(Handle, DC);
  end; (**)
end;

procedure TQBWorkArea.HandleDragEnd(Sender, Target: TObject; X, Y: Integer);
var
  SelectionItem, TargetItem: TQBListItem;
  TargetTbl: TQBTable;
  Obj: TQBFloatObject;
  Pt: TPoint;
begin
  if (Target <> Self) or (Target = nil) then
  begin
    if Assigned(TCustomQueryBuilder(Parent).OnEndDrag) then
    begin
      Pt.x := X;
      Pt.y := Y;
      MapWindowPoints(Handle, Parent.Handle, Pt, 1);
      TCustomQueryBuilder(Parent).OnEndDrag(Sender, Target, Pt.x, Pt.y);
    end;
    Exit;
  end;
  Obj := FSelectionObj;
  if (Obj = nil) or not (Obj is TQBTable) then
    Exit;
  SelectionItem := (Obj as TQBTable).CurrentItem;
  if SelectionItem = nil then
    Exit;
  TargetItem := FTargetDrag;
  if TargetItem = nil then
    Exit;
  TargetTbl := TargetItem.Table;
  AddLink(Obj, TargetTbl, SelectionItem, TargetItem);
  Invalidate;
end;

procedure TQBWorkArea.HandleDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  Pt: TPoint;
begin
  if Source = Sender then
    Exit;
  if Assigned(TCustomQueryBuilder(Parent).OnDragDrop) then
  begin
    Pt.x := X;
    Pt.y := Y;
    MapWindowPoints(Handle, Parent.Handle, Pt, 1);
    TCustomQueryBuilder(Parent).OnDragDrop(Sender, Source, Pt.x, Pt.y);
  end;
end;

function TQBWorkArea.GetDragFieldName: Boolean;
begin
  Result := qboDragFieldName in (Parent as TCustomQueryBuilder).Options;
end;

function TQBWorkArea.GetFont: TFont;
begin
  Result := FFont;
end;

procedure TQBWorkArea.HandleSetFont(Value: TFont);
var
  i: Integer;
  Tbl: TQBTable;
begin
  FFont.Assign(Value);
  for i := 0 to FTables.Count - 1 do
  begin
    Tbl := TQBTable(Tables.Items[I]);
    Tbl.CaptionHeight := Abs(Value.Height) + 4;
    Tbl.ItemHeight := Value.Height;
  end;
  Invalidate;
end;

procedure TQBWorkArea.WMPaint(var Message: TWMPaint);
var
  DC: HDC;
  BoundR: TRect;
  Paint: PAINTSTRUCT;
  Link: TQBLink;
  I: Integer;
  OldColor: TColor;
  Tbl: TQBTable;
begin
  if FUpdating then
    Exit;

  DC := Message.DC;
  if DC = 0 then
    DC := BeginPaint(Handle, Paint);
  try
    SetBkMode(DC, TRANSPARENT);
    OldColor := Brush.Color;
    if Owner is TCustomQueryBuilder then
      Brush.Color := TCustomQueryBuilder(Owner).Palette.WorkSpace;
    SelectObject(DC, GetStockObject(NULL_PEN));
    //    SelectObject(DC,Brush.Handle);
    BoundR := Rect(0, 0, Width, Height);

    for I := 0 to FTables.Count - 1 do
    begin
      Tbl := FTables.Items[I];
      Tbl.Paint(DC);
    end;

    {$IFDEF VCL16}Winapi.Windows{$ELSE}Windows{$ENDIF}.FillRect(DC, BoundR, Brush.Handle);

    Brush.Color := clBlack;
    SelectObject(DC, Brush.Handle);
    SelectObject(DC, FPen.Handle);
    for I := 0 to FLinks.Count - 1 do
    begin
      Link := FLinks.Items[I];
      Link.Paint(DC);
    end;
    Brush.Color := OldColor;
  finally
    if Message.DC = 0 then
      EndPaint(Handle, Paint);
  end;
end;

procedure TQBWorkArea.WMErase(var Message: TMessage);
begin
  inherited;
end;

procedure TQBWorkArea.WMMouseDown(var Message: TWMMouse);
var
  xPos, yPos: Integer;
  Obj: TQBFloatObject;
  Point: TPoint;
  I: Integer;
  Link: TQBLink;
  DC: HDC;
  Rgn, TblRgn: HRGN;
  Rect: TRect;
begin
  inherited;
  if FHint.Visible then
    FHint.ReleaseHandle;
  KillTimer(Handle, 10); //Cancel waiting to show hint
  if CanFocus then SetFocus;

  xPos := HorzScrollBar.Position;
  yPos := VertScrollBar.Position;

  if FSelectionObj <> FCurrentObj then
  begin
    Obj := FSelectionObj;
    if Obj <> nil then
      Obj.Selection := false;
    FSelectionObj := FCurrentObj;
    Obj := FCurrentObj;
    if Obj <> nil then
      Obj.Selection := true;
  end;
  Point.x := Message.XPos;
  Point.y := Message.YPos;

  if FCurrentObj <> nil then
  begin
    Obj := FCurrentObj;
    Obj.MouseDown(Message, FHit);
    if FHit in FProcessHit then
    begin
      if Obj is TQBTable then
      begin
        FmovingX := Point.x - (Obj as TQBTable).Left + xPos;
        FmovingY := Point.y - (Obj as TQBTable).Top + yPos;
        if FHit = htTCaption then
        begin
          DC := GetWindowDC(Handle);
          try
            SelectObject(DC, GetStockObject(BLACK_BRUSH));
            Rgn := CreateRectRgn(0, 0, ClientWidth, ClientHeight);
            Rect := (Obj as TQBTable).BoundsRect;
            OffsetRect(Rect, -xPos, -yPos);
            TblRgn := CreateRectRgnIndirect(Rect);
            CombineRgn(Rgn, Rgn, TblRgn, RGN_DIFF);
            SelectObject(DC, Rgn);
            DeleteObject(Rgn);
            for I := 0 to FLinks.Count - 1 do
            begin
              Link := FLinks.Items[I];
              if (Link.Join.Table[0] = Obj) or (Link.Join.Table[1] = Obj) then
                Link.Paint(DC);
            end;
          finally
            ReleaseDC(Handle, DC);
          end;
        end;
      end
      else
      begin
        FmovingX := Point.x;
        FmovingY := Point.y;
      end;
    end;
  end;

end;

procedure TQBWorkArea.WMMouseMove(var Message: TWMMouse);
var
  I, xPos, yPos: Integer;
  Obj, OldObj: TQBFloatObject;
  Point: TPoint;
  bLeft, bRight, bTop, bBottom: Bool;
  TekHit: TTableHitTest;
begin
  if (FOldX = Message.XPos) and (FOldY = Message.YPos) then
    Exit;
  FOldX := Message.XPos;
  FOldY := Message.YPos;
  xPos := HorzScrollBar.Position;
  yPos := VertScrollBar.Position;

  if FHint.Visible then
    FHint.ReleaseHandle;
  Application.HideHint;
  OldObj := FCurrentObj;
  if OldObj is TQBTable then
  begin
    Point.x := Message.XPos - (OldObj as TQBTable).Left + xPos;
    Point.y := Message.YPos - (OldObj as TQBTable).Top + yPos;
  end
  else
  begin
    Point.x := Message.XPos;
    Point.y := Message.YPos;
  end;
  if (MK_LBUTTON and Message.Keys) <> 0 then
  begin
    if FEditName.Visible then
      FEditName.Visible := false;
    if (FHit in FProcessHit) and (OldObj <> nil) then
    begin
      bLeft := false;
      bRight := false;
      bTop := false;
      bBottom := false;
      case FHit of
        htTLeft: bLeft := true;
        htTTop: bTop := true;
        htTRight: bRight := true;
        htTBottom: bBottom := true;
        htTLeftTop:
          begin
            bLeft := true;
            bTop := true;
          end;
        htTLeftBottom:
          begin
            bLeft := true;
            bBottom := true;
          end;
        htTRightTop:
          begin
            bRight := true;
            bTop := true;
          end;
        htTRightBottom:
          begin
            bRight := true;
            bBottom := true;
          end;
      else //Move
        if OldObj is TQBTable then
        begin
          if Message.XPos + xPos - FmovingX < 0 then
            Point.x := Point.x - Message.XPos - xPos + FmovingX;
          if Message.YPos + yPos - FmovingY < 0 then
            Point.y := Point.y - Message.YPos - yPos + FmovingY;
          if (OldObj as TQBTable).Left + (OldObj as TQBTable).Width +
            Point.x - FmovingX > iHorzSize then
            Point.x := iHorzSize - (OldObj as TQBTable).Left - (OldObj as
              TQBTable).Width + FmovingX;
          if (OldObj as TQBTable).Top + (OldObj as TQBTable).Height +
            Point.y - FmovingY > iVertSize then
            Point.y := iVertSize - (OldObj as TQBTable).Top - (OldObj as
              TQBTable).Height + FmovingY;
          (OldObj as TQBTable).MoveTable(Point.x - FmovingX, Point.y -
            FmovingY);
          //Autoscroll
          if (Message.XPos > ClientWidth) and (Message.XPos + xPos < iHorzSize) then
            HorzScrollBar.Position := HorzScrollBar.Position + Message.XPos -
              ClientWidth;
          if (Message.YPos > ClientHeight) and (Message.YPos + yPos < iVertSize) then
            VertScrollBar.Position := VertScrollBar.Position + Message.YPos -
              ClientHeight;
          if (Message.XPos < 0) and (xPos > 0) then
            HorzScrollBar.Position := HorzScrollBar.Position + Message.XPos;
          if (Message.YPos < 0) and (yPos > 0) then
            VertScrollBar.Position := VertScrollBar.Position + Message.YPos;
          UpdateWindow(Handle);
        end;
      end;
      if bLeft then
      begin //Resize left border
        if Message.XPos + xPos - FmovingX < 0 then
          Point.x := Point.x - Message.XPos - xPos + FmovingX;
        (OldObj as TQBTable).MoveLeft(Point.x - FmovingX);
      end;
      if bTop then
      begin //Resize top border
        if Message.YPos + yPos - FmovingY < 0 then
          Point.y := Point.y - Message.YPos - yPos + FmovingY;
        (OldObj as TQBTable).MoveUpper(Point.y - FmovingY);
      end;
      if bRight then
      begin //Resize right border
        if (OldObj as TQBTable).Left + Point.x > iHorzSize then
          Point.x := iHorzSize - (OldObj as TQBTable).Left;
        (OldObj as TQBTable).Width := Point.x;
      end;
      if bBottom then
      begin //Resize bottom border
        if (OldObj as TQBTable).Top + Point.y > iVertSize then
          Point.y := iVertSize - (OldObj as TQBTable).Top;
        (OldObj as TQBTable).Height := Point.y;
      end;
      if not (OldObj is TQBTable) then
      begin
        FmovingX := Message.XPos;
        FmovingY := Message.YPos;
      end;
    end
    else if OldObj <> nil then
      OldObj.MouseMove(Message, FHit);
    Exit;
  end;

  bTop := false;
  TekHit := htTOutSide;
  Obj := nil;
  SetupCursor(htTOutSide);
  Point.x := Message.XPos;
  Point.y := Message.YPos;
  for I := 0 to FTables.Count - 1 do
  begin
    Obj := FTables.Items[I];
    TekHit := Obj.HitTest(Point);
    if TekHit <> htTOutSide then
    begin
      SetupCursor(TekHit);
      bTop := true;
      Break;
    end;
  end;
  if not bTop then
  begin
    for I := 0 to FLinks.Count - 1 do
    begin
      Obj := FLinks.Items[I];
      TekHit := Obj.HitTest(Point);
      if TekHit <> htTOutSide then
      begin
        SetupCursor(TekHit);
        bTop := true;
        Break;
      end;
    end;
  end;
  if bTop then
  begin
    if (OldObj <> Obj) or (TekHit <> FHit) then
    begin
      if OldObj <> nil then
        OldObj.LeaveHit(Message, FHit);
      FCurrentObj := Obj;
      FHit := TekHit;
    end;
    FDragPoint.x := Message.XPos;
    FDragPoint.y := Message.YPos;
    Obj.MouseMove(Message, TekHit);
    SetTimer(Handle, 10, 500, nil) //Call WMTIMER
  end
  else
  begin
    FCurrentObj := nil;
    if OldObj <> nil then
      OldObj.LeaveHit(Message, FHit);
  end;

end;

procedure TQBWorkArea.WMMouseUp(var Message: TWMMouse);
var
  Link: TQBLink;
  I: Integer;
  Rgn: HRGN;
  Obj: TQBFloatObject;
begin
  inherited;
  Obj := FSelectionObj;
  if (Obj <> nil) and (Obj is TQBTable) and (FHit = htTCaption) then
  begin
    for I := 0 to FLinks.Count - 1 do
    begin
      Link := FLinks.Items[I];
      if (Link.Join.Table[0] = Obj) or (Link.Join.Table[1] = Obj) then
      begin
        Rgn := Link.ClipRgn[0];
        InvalidateRgn(Handle, Rgn, false);
        DeleteObject(Rgn);
      end;
    end;
  end;

  Obj := FCurrentObj;
  if Obj <> nil then
  begin
    Obj.MouseUp(Message, FHit);
  end;

end;

procedure TQBWorkArea.WMMousePopup(var Message: TWMMouse);
var
  bFind: Bool;
{$IFDEF DELPHI5}
  AHandled: boolean;
{$ENDIF}
  TekHit: TTableHitTest;
  Obj: TQBFloatObject;
  Point: TPoint;
  I: Integer;
begin
  bFind := false;
  TekHit := htTOutSide;
  Obj := nil;
  Point.x := Message.XPos;
  Point.y := Message.YPos;
  for I := 0 to FTables.Count - 1 do
  begin
    Obj := FTables.Items[I];
    TekHit := Obj.HitTest(Point);
    if TekHit <> htTOutSide then
    begin
      bFind := true;
      Break;
    end;
  end;
  if not bFind then
  begin
    for I := 0 to FLinks.Count - 1 do
    begin
      Obj := FLinks.Items[I];
      TekHit := Obj.HitTest(Point);
      if TekHit <> htTOutSide then
      begin
        bFind := true;
        Break;
      end;
    end;
  end;
  if bFind then
  begin
{$IFDEF VCL4}
    if FPopupMenu.Items.Count > 0 then
      while FPopupMenu.Items.Count > 0 do
        TCollectionItem(FPopupMenu.Items[FPopupMenu.Items.Count - 1]).Free;
{$ELSE}
    FPopupMenu.Items.Clear;
{$ENDIF}
    ClearSelection();
    Obj.Selection := True;
    FSelectionObj := Obj;
    Obj.PopupMenu(FPopupMenu, Point.x, Point.y, TekHit);
    Point := ClientToScreen(Point);
    if FPopupMenu.Items.Count > 0 then
      FPopupMenu.Popup(Point.x, Point.y);
  {$IFDEF DELPHI5}
  end
  else if Owner is TCustomQueryBuilder then
  begin
    if Assigned(TCustomQueryBuilder(Owner).OnContextPopup) then
    begin
      AHandled := False;
      TCustomQueryBuilder(Owner).OnContextPopup(Owner, Point, AHandled);
    end;
  {$ELSE}
  end
  else if Assigned(Self.PopupMenu) then
  begin
    Point := ClientToScreen(Point);
    Self.PopupMenu.Popup(Point.x, Point.y);
  {$ENDIF}
  end;
end;

procedure TQBWorkArea.WMDblClick(var Message: TWMMouse);
begin
  if FSelectionObj <> nil then
  begin
    CurrentLnk := FSelectionObj;
    if CurrentLnk is TQBLink then
    begin
      if fmLinkProp.ShowModal <> idOk then
        Exit;
      Invalidate;
    end
    else
      (CurrentLnk as TQBTable).DblClick(Message, FHit);
  end;
  inherited;
end;

procedure TQBWorkArea.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  inherited;
  Message.Result := Message.Result or DLGC_WANTALLKEYS or DLGC_WANTARROWS or
    DLGC_WANTTAB;
end;

procedure TQBWorkArea.WMTimer(var Message: TMessage);
var
  MousePt: TPoint;
  Obj: TQBFloatObject;
  R: TRect;
begin
  GetCursorPos(MousePt);
  {$IFDEF VCL16}Winapi.Windows{$ELSE}Windows{$ENDIF}.MapWindowPoints(HWND_DESKTOP, Handle, MousePt, 1);
  if (Message.WParam = 1) or (Message.WParam = 2) then
  begin //AutoScroll on Draging
    MousePt.x := MousePt.x + HorzScrollBar.Position;
    MousePt.y := MousePt.y + VertScrollBar.Position;
    if (MousePt.x <> FDragPoint.x) or (MousePt.y <> FDragPoint.y) then
      KillTimer(Handle, Message.WParam)
    else
    begin
      Obj := FTargetObj;
      if Message.WParam = 1 then
        (Obj as TQBTable).FirstItem := (Obj as TQBTable).FirstItem - 1
      else
        (Obj as TQBTable).FirstItem := (Obj as TQBTable).FirstItem + 1;
    end;
  end;
  if Message.WParam = 10 then
  begin //Show hint
    if (FHit <> htTOutSide) and (FCurrentObj <> nil) then
    begin
      KillTimer(Handle, Message.WParam);
      if (MousePt.x <> FDragPoint.x) or (MousePt.y <> FDragPoint.y) then
      else
      begin
        Obj := FCurrentObj;
        if Obj.Hint <> '' then
        begin
          MousePt := ClientToScreen(MousePt);
          //          if FHint.ParentWindow=0 then FHint.ParentWindow:=Handle;
          R := FHint.CalcHintRect(200, Obj.Hint, nil);
          OffsetRect(R, MousePt.x + 10, MousePt.y + 20);
          FHint.ActivateHint(R, Obj.Hint);
          SetTimer(Handle, 11, 2000, nil);
        end;
      end;
    end;
  end;
  if Message.WParam = 11 then
  begin //Hide hint
    KillTimer(Handle, Message.WParam);
    FHint.ReleaseHandle;
  end;
end;

procedure TQBWorkArea.CMCloseTable(var Message: TMessage);
var
  Tbl: TQBTable;
  I: Integer;
begin
  if not TCustomQueryBuilder(Parent).DoAddRemoveTable(TQBTable(Message.LParam),
    qbaRemove) then
    Exit;
  CMUNLINKTABLE(Message);
  I := FTables.IndexOf(Pointer(Message.LParam));
  if I <> -1 then
  begin
    Tbl := Pointer(Message.LParam);
    if Tbl.Selection then
      FScrollBar.Hide();
    FTables.Delete(I);
    if FCurrentObj = Tbl then
      FCurrentObj := nil;
    if FSelectionObj = Tbl then
    begin
      Tbl.Selection := false;
      FSelectionObj := nil;
    end;
    Tbl.Free;
  end;
end;

procedure TQBWorkArea.CMUnlinkTable(var Message: TMessage);
var
  Tbl: TQBTable;
  Link: TQBLink;
  I: Integer;
begin
  Tbl := Pointer(Message.LParam);
  I := 0;
  while I < FLinks.Count do
  begin
    Link := FLinks.Items[I];
    if (Link.Join.Table[0] = Tbl) or (Link.Join.Table[1] = Tbl) then
    begin
      RemoveLink(Link, I, true);
    end
    else
      Inc(I);
  end;
end;

procedure TQBWorkArea.CMDeleteLink(var Message: TMessage);
begin
  if not TCustomQueryBuilder(Parent).DoAddRemoveLink(TQBLink(Message.LParam),
    qbaRemove) then
    Exit;
  RemoveLink(Pointer(Message.LParam), FLinks.IndexOf(Pointer(Message.LParam)),
    true);
end;

procedure TQBWorkArea.SetupCursor(AHit: TTableHitTest);
begin
  if (AHit = htTLeft) or (AHit = htTRight) then
    Cursor := crSizeWE
  else if (AHit = htTTop) or (AHit = htTBottom) then
    Cursor := crSizeNS
  else if (AHit = htTLeftTop) or (AHit = htTRightBottom) then
    Cursor := crSizeNWSE
  else if (AHit = htTLeftBottom) or (AHit = htTRightTop) then
    Cursor := crSizeNESW
  else if AHit = htTEndLine then
    Cursor := crUpArrow
  else if AHit = htTPoint then
    Cursor := crSizeAll
  else
    Cursor := crDefault;
end;

procedure TQBWorkArea.DoStartDrag(var DragObject: TDragObject);
var
  DragText: aqbString;
  sz: TSize;
  DrawR: TRect;
  Obj: TQBFloatObject;
  I: Integer;
  Addr, AndMask, OrMask: Pointer;
begin
  if Assigned(OnStartDrag) then
    OnStartDrag(Self, DragObject);
  if DragFieldName then
  begin
    Obj := FSelectionObj;
    if (Obj = nil) or not (Obj is TQBTable) then
      Exit;
    if (Obj as TQBTable).CurrentItem = nil then
      Exit;
    DragText := (Obj as TQBTable).Alias + '.' + (Obj as
      TQBTable).CurrentItem.Caption;

    sz := FCursorBMP.Canvas.TextExtent(DragText);
    FCursorBMP.Width := (sz.cx + 4) div 16;
    if FCursorBMP.Width * 16 <> sz.cx + 4 then
      FCursorBMP.Width := (FCursorBMP.Width + 1) * 16
    else
      FCursorBMP.Width := sz.cx + 4;
    FCursorBMP.Height := sz.cy + 4;

    DrawR := Rect(0, 0, FCursorBMP.Width, FCursorBMP.Height);
{$IFDEF VCL4}
    FCursorBMP.Canvas.Rectangle(DrawR.Left, DrawR.Top, DrawR.Right, DrawR.Bottom);
{$ELSE}
    FCursorBMP.Canvas.Rectangle(DrawR);
{$ENDIF}
    QBDrawText(FCursorBMP.Canvas.Handle, @DragText[1], Length(DragText),
      DrawR, DT_CENTER or DT_SINGLELINE or DT_VCENTER);

    sz.cx := FCursorBMP.Width * FCursorBMP.Height div 8;

    GetMem(AndMask, sz.cx);
    for I := 0 to FCursorBMP.Height - 1 do
    begin
      Addr := Pointer(Integer(AndMask) + I * FCursorBMP.Width div 8);
      OrMask := FCursorBMP.ScanLine[I];
      Move(OrMask^, Addr^, FCursorBMP.Width div 8);
    end;
    GetMem(OrMask, sz.cx);
    FillChar(OrMask^, sz.cx, 0);

    FExDragCur := CreateCursor(HInstance, FCursorBMP.Width div 2,
      FCursorBMP.Height div 2,
      FCursorBMP.Width, FCursorBMP.Height, AndMask, OrMask);
    if FExDragCur <> 0 then
      Screen.Cursors[-12] := FExDragCur;
    FreeMem(AndMask);
    FreeMem(OrMask);
  end
  else
    Screen.Cursors[-12] := FDragCur;
end;

procedure TQBWorkArea.DoEndDrag(Target: TObject; X, Y: Integer);
begin
  inherited;
  Screen.Cursors[-12] := FDefDrag;
  FHiliteItem := nil;
  if DragFieldName then
    DestroyCursor(FExDragCur);
end;

procedure TQBWorkArea.KeyDown(var Key: Word; Shift: TShiftState);
var
  Obj: TQBFloatObject;
  I: Integer;
begin
  inherited;
  Obj := FSelectionObj;
  if FHint.Visible then
    FHint.ReleaseHandle();
  Application.HideHint();
  if (Key = VK_TAB) and (Tables.Count > 0) then
  begin
    I := FTables.IndexOf(FSelectionObj);
    if (ssShift in Shift) then
      Inc(I)
    else
      I := FTables.Count - 1;
    if I < 0 then
      I := 0;
    if I >= FTables.Count then
      I := FTables.Count - 1;
    FSelectionObj := FTables.Items[I];
    if FSelectionObj <> Obj then
    begin
      if Obj <> nil then
        Obj.Selection := false;
      Obj := FSelectionObj;
      Obj.Selection := true;
    end;
    Exit;
  end
  else if Key = VK_DELETE then
  begin
    FSelectionObj := nil;
    if Obj is TQBTable then
      TQBTable(Obj).Close()
    else if Obj is TQBLink then
      TQBLink(Obj).Remove();
    Obj := nil;
  end;
  if Obj <> nil then
    Obj.KeyDown(Key, Shift);
end;

procedure TQBWorkArea.KeyUp(var Key: Word; Shift: TShiftState);
var
  Obj: TQBFloatObject;
begin
  inherited;
  Obj := FSelectionObj;
  if Obj <> nil then
  begin
    Obj := FSelectionObj;
    Obj.KeyUp(Key, Shift);
  end;
end;

procedure TQBWorkArea.SetFont(const Value: TFont);
begin
  if FFont <> Value then
    HandleSetFont(Value);
end;

procedure TQBWorkArea.ClearSelection;
var
  I: integer;
begin
  for I := 0 to FTables.Count - 1 do
    if Assigned(TQBTable(FTables[I])) and TQBTable(FTables[I]).Selection then
      TQBTable(FTables[I]).Selection := False;
  for I := 0 to FLinks.Count - 1 do
    if Assigned(TQBLink(FLinks[I])) and TQBLink(FLinks[I]).Selection then
      TQBLink(FLinks[I]).Selection := False;
end;

function TQBWorkArea.FindLinkByID(const ID: aqbString): integer;
var
  I: integer;
begin
  Result := -1;
  for I := 0 to FLinks.Count - 1 do
    if Assigned(TQBLink(FLinks[I])) and (TQBLink(FLinks[I]).LinkID = ID) then
    begin
      Result := I;
      Break;
    end;
end;

procedure TQBWorkArea.BeginUpdate;
begin
  FUpdating := True;
end;

procedure TQBWorkArea.EndUpdate;
begin
  FUpdating := False;
  Invalidate();
end;

function TQBWorkArea.GetParser: TQBSelectParser;
begin
  if Owner is TCustomQueryBuilder then
    Result := TCustomQueryBuilder(Owner).Parser
  else
    Result := nil;
end;

function TQBWorkArea.SameIdentifier(const Ident1, Ident2: aqbString): boolean;
begin
  if (Parser <> nil) and not Parser.CaseSensitiveIdentifiers then
    Result := QBCompareText(Ident1, Ident2) = 0
  else
    Result := QBCompareStr(Ident1, Ident2) = 0;
end;

function TQBWorkArea.AddPanelAt(const AName, AAlias: aqbString;
  ADataSet: TDataSet; ATopLeft: TPoint): Pointer;
begin
  Result := AddPanelInternal(AName, AAlias, ADataset, false, ATopLeft);
end;

function TQBWorkArea.AddPanelInternal(const AName, AAlias: aqbString;
  ADataSet: TDataSet; UseDefaultPos: boolean; ATopLeft: TPoint): Pointer;
var
  Tt, Tbl: TQBTable;
  TekRect, SrcRect, DestRect: TRect;
  I,
  HeightTmp: Integer;
  S: aqbString;
begin
  S := AAlias;
  if Trim(S) = '' then
    S := AName;

  S := GenerateUniqueAlias(S, sTableAlias);

  if (FSelectionObj <> nil) and (TObject(FSelectionObj) is TQBFloatObject) then
    TQBFloatObject(FSelectionObj).Selection := false;

  Tt := TQBTable.Create(Self, ADataSet, AName);
  if AName <> S then
    Tt.Alias := S;

  if not TCustomQueryBuilder(Parent).DoAddRemoveTable(Tt, qbaAdd) then
  begin
    Tt.Free;
    Result := nil;
    Exit;
  end;

  TekRect := Tt.CaptionRect(true);
  if Tt.Width < TekRect.Right - TekRect.Left then
    Tt.Width := TekRect.Right - TekRect.Left;
  TekRect := Tt.BoundsRect;
  I := FObjSavedArrangements.IndexOf(Tt.Alias);
  if I <> -1 then
  begin
    TekRect.Right := TekRect.Left + FObjSavedArrangements[I].Width - 1;
    TekRect.Bottom := TekRect.Top + FObjSavedArrangements[I].Height - 1;
    OffsetRect(TekRect, FObjSavedArrangements[I].Left,
      FObjSavedArrangements[I].Top);
    Tt.Expand := FObjSavedArrangements[I].Expanded;
  end
  else
    OffsetRect(TekRect, 50, 20);
  if UseDefaultPos then
  begin
    I := 0;
    while I < FTables.Count do
    begin
      Tbl := FTables.Items[I];
      Inc(I);
      SrcRect := Tbl.BoundsRect;
      if (IntersectRect(DestRect, SrcRect, TekRect)) then
      begin
        OffsetRect(TekRect, SrcRect.Right + 10 - TekRect.Left, 0);

        if TekRect.Right >= Width then
        begin
          TekRect.Right := TekRect.Right - TekRect.Left + 50;
          TekRect.Left := 50;
          HeightTmp := TekRect.Bottom - TekRect.Top;
          TekRect.Top :=  TekRect.Bottom + 10;
          TekRect.Bottom := TekRect.Top + HeightTmp;
        end;
        I := 0;
      end;
    end;
  end
  else
    OffsetRect(TekRect, ATopLeft.X - TekRect.Left, ATopLeft.Y - TekRect.Top);
  if TekRect.Top < 0 then
    OffsetRect(TekRect, 0, -TekRect.Top);
  if TekRect.Left < 0 then
    OffsetRect(TekRect, -TekRect.Left, 0);

  Tt.TopLeft := TekRect.TopLeft;
  Tt.Width := TekRect.Right - TekRect.Left + 1;
  Tt.Height := TekRect.Bottom - TekRect.Top + 1;
  FTables.Insert(0, Tt);
  if Assigned(FOnAddTable) then
    FOnAddTable(Self, Tt);
  Tt.Selection := true;
  FSelectionObj := Tt;
  //igorp
  CheckSubQueryTable('', ADataSet, True);
  //\igorp
  Result := Tt;
end;

function TQBWorkArea.AddProcAt(const AName, AAlias: aqbString;
  ADataSet: TDataSet; AParams: TParams; ATopLeft: TPoint): Pointer;
begin
  Result := AddProcInternal(AName, AAlias, ADataSet, AParams, false, ATopLeft);
end;

function TQBWorkArea.AddProcInternal(const AName, AAlias: aqbString;
  ADataSet: TDataSet; AParams: TParams; UseDefaultPos: boolean;
  ATopLeft: TPoint): Pointer;
var
  Tbl: TQBTable;
  SP: TQBStoredProc;
  TekRect, SrcRect, DestRect: TRect;
  I: Integer;
  S: aqbString;
begin
  S := AAlias;
  if Trim(S) = '' then
    S := AName;

  S := GenerateUniqueAlias(S, sProcAlias);

  if (FSelectionObj <> nil) and (TObject(FSelectionObj) is TQBFloatObject) then
    TQBFloatObject(FSelectionObj).Selection := false;
  SP := TQBStoredProc.Create(Self, ADataSet, AName, AParams);
  if AName <> S then
    SP.Alias := S;
  if not TCustomQueryBuilder(Parent).DoAddRemoveTable(SP, qbaAdd) then
  begin
    SP.Free;
    Result := nil;
    Exit;
  end;

  TekRect := SP.CaptionRect(true);
  if SP.Width < TekRect.Right - TekRect.Left then
    SP.Width := TekRect.Right - TekRect.Left;
  TekRect := SP.BoundsRect;
  // ab new 17.02.2004
  I := FObjSavedArrangements.IndexOf(SP.Alias);
  if I <> -1 then
  begin
    TekRect.Right := TekRect.Left + FObjSavedArrangements[I].Width - 1;
    TekRect.Bottom := TekRect.Top + FObjSavedArrangements[I].Height - 1;
    OffsetRect(TekRect, FObjSavedArrangements[I].Left,
      FObjSavedArrangements[I].Top);
    SP.Expand := FObjSavedArrangements[I].Expanded;
  end
  else
    OffsetRect(TekRect, 50, 20);
  if UseDefaultPos then
  begin
    I := 0;
    while I < FTables.Count do
    begin
      Tbl := FTables.Items[I];
      Inc(I);
      SrcRect := Tbl.BoundsRect;
      if (IntersectRect(DestRect, SrcRect, TekRect)) then
      begin
        OffsetRect(TekRect, SrcRect.Right + 10 - TekRect.Left, 0);
        I := 0;
      end;
    end;
  end
  else
    OffsetRect(TekRect, ATopLeft.X - TekRect.Left, ATopLeft.Y - TekRect.Top);
  if TekRect.Top < 0 then
    OffsetRect(TekRect, 0, -TekRect.Top);
  if TekRect.Left < 0 then
    OffsetRect(TekRect, -TekRect.Left, 0);
  SP.TopLeft := TekRect.TopLeft;
  SP.Width := TekRect.Right - TekRect.Left + 1;
  SP.Height := TekRect.Bottom - TekRect.Top + 1;
  FTables.Insert(0, SP);
  if Assigned(FOnAddTable) then
    FOnAddTable(Self, SP);
  SP.Selection := true;
  FSelectionObj := SP;
  Result := SP;
end;

function TQBWorkArea.DoMouseWheelDown(Shift: TShiftState;
  MousePos: TPoint): Boolean;
begin
  if Shift = [] then
  begin
    VertScrollBar.Position := VertScrollBar.Position + Mouse.WheelScrollLines * 20;
    Result := true;
  end
  else if Shift = [ssShift] then
  begin
    HorzScrollBar.Position := HorzScrollBar.Position + Mouse.WheelScrollLines * 20;
    Result := true;
  end
  else
    Result := false;
end;

function TQBWorkArea.DoMouseWheelUp(Shift: TShiftState;
  MousePos: TPoint): Boolean;
begin
  if Shift = [] then
  begin
    VertScrollBar.Position := VertScrollBar.Position - Mouse.WheelScrollLines * 20;
    Result := true;
  end
  else if Shift = [ssShift] then
  begin
    HorzScrollBar.Position := HorzScrollBar.Position - Mouse.WheelScrollLines * 20;
    Result := true;
  end
  else
    Result := false;
end;

procedure TQBWorkArea.CloseNameEditor;
begin
  if EditName.Visible then
    HandleEditNameKillFocus(nil);
end;

{ TQBObjectArrangements }

function TQBObjectArrangements.Add: TQBObjectArrangement;
begin
  Result := TQBObjectArrangement(inherited Add);
end;

function TQBObjectArrangements.IndexOf(const Alias: aqbString): integer;
var
  I: integer;
begin
  Result := -1;
  for I := 0 to Count - 1 do
    if Items[I].Alias = Alias then
    begin
      Result := I;
      Break;
    end;
end;

function TQBObjectArrangements.GetItem(Index: Integer): TQBObjectArrangement;
begin
  Result := TQBObjectArrangement(inherited GetItem(Index));
end;

procedure TQBObjectArrangements.SetItem(Index: Integer;
  const Value: TQBObjectArrangement);
begin
  inherited SetItem(Index, Value);
end;

{ TQBFloatObject }

constructor TQBFloatObject.Create(AParent: TQBWorkArea);
begin
  inherited Create;
  FParent := AParent;
  FSelection := false;
end;

procedure TQBFloatObject.SetSelect(Value: Boolean);
begin
  FSelection := Value;
  if Value then
    FParent.TableToFront(Self); //Select table
end;

procedure TQBFloatObject.MouseDown(var Msg: TWMMouse; Hit: TTableHitTest);
var
  btn: TMouseButton;
  shft: TShiftState;
begin
  btn := mbLeft;
  shft := [];
  if (MK_LBUTTON and Msg.Keys) <> 0 then
  begin
    btn := mbLeft;
    shft := shft + [ssLeft];
  end;
  if (MK_RBUTTON and Msg.Keys) <> 0 then
  begin
    btn := mbRight;
    shft := shft + [ssRight];
  end;
  if (MK_MBUTTON and Msg.Keys) <> 0 then
  begin
    btn := mbMiddle;
    shft := shft + [ssMiddle];
  end;
  if (MK_CONTROL and Msg.Keys) <> 0 then
    shft := shft + [ssCtrl];
  if (MK_SHIFT and Msg.Keys) <> 0 then
    shft := shft + [ssShift];
  if Assigned(FOnMouseDown) then
    FOnMouseDown(Self, btn, shft, Msg.XPos, Msg.YPos);
  Msg.Result := 1;
end;

procedure TQBFloatObject.MouseMove(var Msg: TWMMouse; Hit: TTableHitTest);
var
  shft: TShiftState;
begin
  shft := [];
  if (MK_LBUTTON and Msg.Keys) <> 0 then
    shft := shft + [ssLeft];
  if (MK_RBUTTON and Msg.Keys) <> 0 then
    shft := shft + [ssRight];
  if (MK_MBUTTON and Msg.Keys) <> 0 then
    shft := shft + [ssMiddle];
  if (MK_CONTROL and Msg.Keys) <> 0 then
    shft := shft + [ssCtrl];
  if (MK_SHIFT and Msg.Keys) <> 0 then
    shft := shft + [ssShift];
  if Assigned(FOnMouseMove) then
    FOnMouseMove(Self, shft, Msg.XPos, Msg.YPos);
  Msg.Result := 1;
end;

procedure TQBFloatObject.MouseUp(var Msg: TWMMouse; Hit: TTableHitTest);
var
  btn: TMouseButton;
  shft: TShiftState;
begin
  btn := mbLeft;
  shft := [];
  if (MK_LBUTTON and Msg.Keys) <> 0 then
  begin
    btn := mbLeft;
    shft := shft + [ssLeft];
  end;
  if (MK_RBUTTON and Msg.Keys) <> 0 then
  begin
    btn := mbRight;
    shft := shft + [ssRight];
  end;
  if (MK_MBUTTON and Msg.Keys) <> 0 then
  begin
    btn := mbMiddle;
    shft := shft + [ssMiddle];
  end;
  if (MK_CONTROL and Msg.Keys) <> 0 then
    shft := shft + [ssCtrl];
  if (MK_SHIFT and Msg.Keys) <> 0 then
    shft := shft + [ssShift];
  if Assigned(FOnMouseUp) then
    FOnMouseUp(Self, btn, shft, Msg.XPos, Msg.YPos);
  Msg.Result := 1;
  Parent.Refresh; 
end;

procedure TQBFloatObject.KeyDown(var Key: Word; Shift: TShiftState);
begin
  if Assigned(FOnKeyDown) then
    FOnKeyDown(Self, Key, Shift);
end;

procedure TQBFloatObject.KeyUp(var Key: Word; Shift: TShiftState);
begin
  if Assigned(FOnKeyUp) then
    FOnKeyUp(Self, Key, Shift);
end;

procedure TQBFloatObject.LeaveHit(var Msg: TWMMouse; Hit: TTableHitTest);
begin
  //Abstract
end;

initialization

finalization

end.
